package main

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"io"
	"os"
	"time"
)

import bencode "github.com/jackpal/bencode-go"

type FileDict struct {
	Length int64    "length"
	Path   []string "path"
	Md5sum string   "md5sum"
}

type InfoDict struct {
	FileDuration []int64 "file-duration"
	FileMedia    []int64 "file-media"

	// Single file
	Name   string "name"
	Length int64  "length"
	Md5sum string "md5sum"

	// Multiple files
	Files       []FileDict "files"
	PieceLength int64      "piece length"
	Pieces      string     "pieces"
	Private     int64      "private"
}

type MetaInfo struct {
	Info         InfoDict   "info"
	InfoHash     string     "info hash"
	Announce     string     "announce"
	AnnounceList [][]string "announce-list"
	CreationDate int64      "creation date"
	Comment      string     "comment"
	CreatedBy    string     "created by"
	Encoding     string     "encoding"
}

func Parser(threadId string) {
	filename, ok := <-torrentList
	fmt.Println("[解析线程-" + threadId + "] 启动")
	for ok {

		// 解析文件
		parseTorrent(filename)

		// 再次获取种子
		filename, ok = <-torrentList
	}
	fmt.Println("[检测线程-" + threadId + "] 完成")
	thread <- -1
}

func (metaInfo *MetaInfo) ReadTorrentMetaInfoFile(r io.Reader) bool {

	fileMetaData, er := bencode.Decode(r)
	if er != nil {
		return false
	}

	metaInfoMap, ok := fileMetaData.(map[string]interface{})
	if !ok {
		return false
	}

	var bytesBuf bytes.Buffer
	for mapKey, mapVal := range metaInfoMap {
		switch mapKey {
		case "info":
			if er = bencode.Marshal(&bytesBuf, mapVal); er != nil {
				return false
			}

			infoHash := sha1.New()
			infoHash.Write(bytesBuf.Bytes())
			metaInfo.InfoHash = string(infoHash.Sum(nil))

			if er = bencode.Unmarshal(&bytesBuf, &metaInfo.Info); er != nil {
				return false
			}

		case "announce-list":
			if er = bencode.Marshal(&bytesBuf, mapVal); er != nil {
				return false
			}
			if er = bencode.Unmarshal(&bytesBuf, &metaInfo.AnnounceList); er != nil {
				return false
			}

		case "announce":
			if aa, ok := mapVal.(string); ok {
				metaInfo.Announce = aa
			}

		case "creation date":

			if tt, ok := mapVal.(int64); ok {
				metaInfo.CreationDate = tt
			}

		case "comment":
			if cc, ok := mapVal.(string); ok {
				metaInfo.Comment = cc
			}

		case "created by":
			if cb, ok := mapVal.(string); ok {
				metaInfo.CreatedBy = cb
			}

		case "encoding":
			if ed, ok := mapVal.(string); ok {
				metaInfo.Encoding = ed
			}
		}
	}

	return true
}

func parseTorrent(filename string) (int, error) {

	fp, err := os.Open(filename)
	if err != nil {
		fmt.Println("打开文件 "+filename+" 错误:", err)
	}
	//defer fp.Close()

	var metaTorrent MetaInfo
	ok := metaTorrent.ReadTorrentMetaInfoFile(fp)
	fp.Close()
	if !ok {
		fmt.Println("种子 " + filename + " 不合法，将移动到 ErrorTorrent 文件夹")
		MoveFile(filename, "ErrorTorrent/"+filename)
		return 3, nil
	}

	name := metaTorrent.Info.Name
	hashInfo := fmt.Sprintf("%x", metaTorrent.InfoHash)
	created := metaTorrent.CreationDate

	var data Data
	data.hash = hashInfo
	//data.magnet = "magnet:?xt=urn:btih:" + hashInfo + "[CodEiT.cn]" + name
	data.title = name
	data.time = created
	data.indexTime = time.Now().Unix()

	if metaTorrent.Info.Length != 0 { // 单文件
		data.file = name
		data.fileNumber = 1
		data.fileSize = metaTorrent.Info.Length
	} else { // 多文件
		var fileLength int64 = 0
		var total int = 0 // 文件数量
		var fileDownLoadList bytes.Buffer

		for _, fileDict := range metaTorrent.Info.Files {
			fileLength += fileDict.Length
			for _, path := range fileDict.Path {
				fileDownLoadList.WriteString(path)
				fileDownLoadList.WriteString(",")
				total = total + 1
			}
		}
		data.file = fileDownLoadList.String()
		data.fileNumber = total
		data.fileSize = fileLength
	}

	// 解析结果写入信道
	result <- data

	return 0, nil
}

func MoveFile(srcName, dstName string) {
	err := os.Rename(srcName, dstName)
	if err != nil {
		fmt.Println(err)
	}
}
